//////////////////////////////////////////////////////////////////////////////////
//	Copyright 2011 by Yosef Grabivker, ryuho@homenetworksoft.com
//
//   Licensed under the Apache License, Version 2.0 (the "License");
//   you may not use this file except in compliance with the License.
//   You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
//   Unless required by applicable law or agreed to in writing, software
//   distributed under the License is distributed on an "AS IS" BASIS,
//   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//   See the License for the specific language governing permissions and
//   limitations under the License.
//////////////////////////////////////////////////////////////////////////////////

#include <wx/wx.h>
#include <wx/html/htmlwin.h>
#include <wx/numdlg.h>
#include <wx/imaglist.h>

#include <ctype.h>
#include <algorithm>
#include <sstream>
#include "statctrl.h"
#include "connectionstab.h"
#include "searchpanel.h"
#include "utils.h"
#include "rcmdapp.h"
#include "mainwnd.h"
#include "levelgraph.h"
#include "guicommon.h"

#ifdef _DEBUG
#include <iostream>
#include <fstream>
#endif

#ifdef LINUX
#include <arpa/inet.h>
#endif

#define GRAPH_TOTAL_NAME	_("Total")
#define	GRAPH_TCP_NAME		_("TCP")
#define GRAPH_UDP_NAME		_("UDP")
#define GRAPH_ICMP_NAME		_("ICMP")
#define GRAPH_OTHR_NAME		_("Other")



using namespace std;

#define OPT_CHECK_INT 60*60 /*one hour*/

#ifndef LINUX
#pragma warning (disable: 4996)
#endif

enum cListColumns  { cListColumnProto=0, cListColumnTTL, cListColumnState, cListColumnSrcCountry, cListColumnSrc, cListColumnSrcPort, cListColumnDst, cListColumnDstCountry, cListColumnDstPort };

enum cIDS
{
	ID_LIST=8000,
	ID_ANYLIZE,
	ID_BTN_INFO,
	ID_RESOLVE
};

bool	CConnectionsTab::m_bSortAsc = true;

static SEARCHFIELD sf[]=
{
	{ cSearchTypeString,	cListColumnProto,	_("Protocol")	},
	{ cSearchTypeNumeric,	cListColumnTTL,		_("TTL(sec)")	},
	{ cSearchTypeString,	cListColumnState,	_("State")		},
	{ cSearchTypeString,	cListColumnSrc,		_("Src Country")},
	{ cSearchTypeString,	cListColumnSrc,		_("Source")		},
	{ cSearchTypeNumeric,	cListColumnSrcPort,	_("Src Port")	},
	{ cSearchTypeNumeric,	cListColumnDstPort,	_("Dst Country")},
	{ cSearchTypeString,	cListColumnDst,		_("Destination")},
	{ cSearchTypeNumeric,	cListColumnDstPort,	_("Dst Port")	},
};

typedef struct tagTCPCHECK
{
	const wxChar* strSTATE;
	size_t		stTimeOutNormal;
	size_t		stTimeOutRouter;
	const wxChar* strAction;
}TCPCHECK;

static NATTIMEOUTS	tcpDefault;
static TCPCHECK		tcpCheck[] = 
{
	{ wxT("Generic"),		tcpDefault.stGeneric,		0,	wxT("ip_conntrack_generic_timeout")			}, 
	{ wxT("ESTABLISHED"),	tcpDefault.stESTABLISHED,	0,	wxT("ip_conntrack_tcp_timeout_established") }, 
	{ wxT("SYN_SENT"),		tcpDefault.stSYN_SENT,		0,	wxT("ip_conntrack_tcp_timeout_syn_sent")	},
	{ wxT("SYN_RECV"),		tcpDefault.stSYN_RECV,		0,	wxT("ip_conntrack_tcp_timeout_syn_recv")	},
	{ wxT("FIN_WAIT"),		tcpDefault.stFIN_WAIT,		0,	wxT("ip_conntrack_tcp_timeout_fin_wait")	},
	{ wxT("TIME_WAIT"),		tcpDefault.stTIME_WAIT,		0,	wxT("ip_conntrack_tcp_timeout_time_wait")	},
	{ wxT("CLOSE"),			tcpDefault.stCLOSE,			0,	wxT("ip_conntrack_tcp_timeout_close")		},
	{ wxT("CLOSE_WAIT"),	tcpDefault.stCLOSE_WAIT,	0,	wxT("ip_conntrack_tcp_timeout_close_wait")	},
	{ wxT("LAST_ACK"),		tcpDefault.stLAST_ACK,		0,	wxT("ip_conntrack_tcp_timeout_last_ack")	}
};

BEGIN_EVENT_TABLE(CConnectionsTab, wxPanel)
	EVT_ACTIVATE( CConnectionsTab::OnActivate)
	EVT_LIST_COL_CLICK(ID_LIST, CConnectionsTab::OnColumn)
	EVT_HTML_LINK_CLICKED(ID_ANYLIZE, CConnectionsTab::OnCLick)
	EVT_UPDATE_UI(ID_BTN_INFO, CConnectionsTab::OnInfoUI)
	EVT_BUTTON(ID_BTN_INFO, CConnectionsTab::OnInfo)
	EVT_CHECKBOX(ID_RESOLVE, CConnectionsTab::OnResolveCheckBox)
END_EVENT_TABLE()

CConnectionsTab::CConnectionsTab(class wxWindow* pParent):wxPanel(pParent),
	m_lSearchResLine(SEARCH_NOT_FOUND), CSearchImpl<CConnectionsTab>(*this),
	m_tInt( 300 ), m_bResolve(false)
{
	m_lTotal = 0;
	m_lTCP = 0;
	m_lUDP = 0;
	m_lICMP = 0;
	m_lOther = 0;

	wxBoxSizer* sizer = new wxBoxSizer(wxVERTICAL);

	m_pSearch = new CSearchPanel(this, this);
	sizer->Add(m_pSearch, 0, wxALL|wxEXPAND);

	m_nSortCol= cListColumnProto;
	m_pList = new CLogCtrl<CConnectionsTab>(this, ID_LIST);

	SetSearchListPtr(m_pList);

	sizer->Add(m_pList, 1, wxALL|wxEXPAND);

	for (size_t i=0;i<sizeof(sf)/sizeof(sf[0]);i++)
	{
		m_pList->InsertColumn( sf[i].iFieldID, sf[i].strLabel );
	}

	m_pList->SetColumnWidth( cListColumnProto,		80  );
	m_pList->SetColumnWidth( cListColumnTTL,		60  );
	m_pList->SetColumnWidth( cListColumnSrcCountry,	60  );
	m_pList->SetColumnWidth( cListColumnSrc,		120 );
	m_pList->SetColumnWidth( cListColumnSrcPort,	120 );
	m_pList->SetColumnWidth( cListColumnDstCountry,	60  );
	m_pList->SetColumnWidth( cListColumnDst,		120 );
	m_pList->SetColumnWidth( cListColumnDstPort,	120 );

	wxBoxSizer* sizerLine = new wxBoxSizer(wxHORIZONTAL);

	m_bResolve = wxGetApp().GetMainWnd()->GetConfig().GetIPResolve();
	m_pResolveNames = new wxCheckBox(this, ID_RESOLVE, _("Resolve IP") );
	m_pResolveNames->SetValue( m_bResolve );
	sizerLine->Add(m_pResolveNames, 0, wxLEFT|wxALIGN_CENTRE|wxALIGN_LEFT, 10);

	//sizerLine->AddStretchSpacer(1);
	sizerLine->AddSpacer(10);

	m_pInfoQuery = new wxButton(this, ID_BTN_INFO, _("Click to request IP address details (will open browser window)"),
		wxDefaultPosition, wxDefaultSize, wxNO_BORDER);
	sizerLine->Add(m_pInfoQuery, 1, wxRIGHT|wxEXPAND|wxALIGN_CENTRE|wxALIGN_RIGHT, 10);


	sizer->Add(sizerLine, 0, wxALL|wxEXPAND|wxALIGN_CENTRE);


/////////////////////////////////////////////////////////////////////////////////
	int nFlags=wxLEFT|wxRIGHT|wxDOWN|wxEXPAND|wxALIGN_CENTRE_VERTICAL;
	int nBorder=8;
	wxString label = _("Protocols graph");

	wxStaticBoxSizer* pStaticBox = new wxStaticBoxSizer( new wxStaticBox(this, wxID_ANY,label), wxVERTICAL);

	//Add Time selector
	m_pConGraph = new CLevelGraph(this, CLevelGraph::cNormalGraphMode, CLevelGraph::cGraphLabelRight, wxID_ANY);

	wxBoxSizer* pTitleBox = CGuiCommon::GraphicHeader(this, label, m_pConGraph );
	pStaticBox->Add(pTitleBox, 0, wxALIGN_RIGHT, 0);
	pStaticBox->Add( m_pConGraph, 1, nFlags, nBorder );
	sizer->Add( pStaticBox, 1, nFlags|wxUP, nBorder );

	m_pConGraph->SetColor(GRAPH_TOTAL_NAME,	wxColor(0, 0,205)		);
	m_pConGraph->SetColor(GRAPH_TCP_NAME,	wxColor(255, 99,71)		);
	m_pConGraph->SetColor(GRAPH_UDP_NAME,	wxColor(220,20,60)		);
	m_pConGraph->SetColor(GRAPH_ICMP_NAME,	wxColor(142, 56, 142)	);
	m_pConGraph->SetColor(GRAPH_OTHR_NAME,	*wxBLACK	);

	m_pConGraph->SetMesureName(wxT(""));
/////////////////////////////////////////////////////////////////////////////////
	m_pAnylizeWindow = new wxHtmlWindow(this,ID_ANYLIZE, wxDefaultPosition, wxSize(200,100), wxHW_SCROLLBAR_AUTO );
	sizer->Add(m_pAnylizeWindow, 0, wxALL|wxEXPAND);

	SetSizer(sizer);

	ShowAnalyze();

	Reset();

	SetTimeInterval( m_tInt );
	
	m_pList->SetImageList(m_ip2Country.GetFlagImageList(), wxIMAGE_LIST_SMALL);
	m_pList->SetImageList(m_ip2Country.GetFlagImageList(), wxIMAGE_LIST_STATE);
	((CLogCtrl<CConnectionsTab>*)m_pList)->DisableStrip();

	m_resolver.Start();

	m_resolver.SetObserver( this );
}

CConnectionsTab::~CConnectionsTab(void)
{
	m_resolver.SetObserver( NULL );
	m_resolver.Stop();
}

void CConnectionsTab::SetTimeInterval(size_t tInt)
{
	m_tInt = tInt;
	m_pConGraph->SetTimePeriod(m_tInt);
}


void CConnectionsTab::OnActivate(wxActivateEvent&)
{
	
}

bool IpCompare(const wxString& a, const wxString& b)
{
	bool bRes(false);

	if ( a!=b )
	{
		unsigned int a1(0),a2(0),a3(0),a4(0);
		unsigned int b1(0),b2(0),b3(0),b4(0);

		sscanf( a.ToAscii(), "%u.%u.%u.%u", &a1,&a2,&a3,&a4);
		sscanf( b.ToAscii(), "%u.%u.%u.%u", &b1,&b2,&b3,&b4);

		return a4+(a3<<8)+(a2<<16) +(a1<<24)>b4+(b3<<8)+(b2<<16) +(b1<<24);
		
	}

	return bRes;
}

bool SortProto( const CONNTRACENTRY& a, const CONNTRACENTRY& b )
{
	return CConnectionsTab::m_bSortAsc?a.strProto > b.strProto:a.strProto < b.strProto;
}

bool SortSrcCountry( const CONNTRACENTRY& a, const CONNTRACENTRY& b )
{
	return  CConnectionsTab::m_bSortAsc?a.strSrcCountry>b.strSrcCountry:a.strSrcCountry<b.strSrcCountry;
}

bool SortDstCountry( const CONNTRACENTRY& a, const CONNTRACENTRY& b )
{
	return  CConnectionsTab::m_bSortAsc?a.strDstCountry>b.strDstCountry:a.strDstCountry<b.strDstCountry;
}

bool SortSrc( const CONNTRACENTRY& a, const CONNTRACENTRY& b )
{
	return  CConnectionsTab::m_bSortAsc?IpCompare(a.strSrc, b.strSrc):IpCompare(b.strSrc, a.strSrc);
}

bool SortSrcPort( const CONNTRACENTRY& a, const CONNTRACENTRY& b )
{
	long ad(0), bd(0);

	a.strSrcPort.ToLong(&ad);
	b.strSrcPort.ToLong(&bd);

	return CConnectionsTab::m_bSortAsc? ad>bd:ad<bd;
}

bool SortDst( const CONNTRACENTRY& a, const CONNTRACENTRY& b )
{
	return  CConnectionsTab::m_bSortAsc?IpCompare(a.strDst, b.strDst):IpCompare(b.strDst, a.strDst);
}

bool SortDstPort( const CONNTRACENTRY& a, const CONNTRACENTRY& b )
{
	long ad(0), bd(0);

	a.strDstPort.ToLong(&ad);
	b.strDstPort.ToLong(&bd);

	return CConnectionsTab::m_bSortAsc? ad>bd:ad<bd;
}

bool SortTTL( const CONNTRACENTRY& a, const CONNTRACENTRY& b )
{
	long ad(0), bd(0);

	a.strTTL.ToLong(&ad);
	b.strTTL.ToLong(&bd);

	return CConnectionsTab::m_bSortAsc? ad>bd:ad<bd;
}

bool SortState( const CONNTRACENTRY& a, const CONNTRACENTRY& b )
{
	return CConnectionsTab::m_bSortAsc? a.strStatus>b.strStatus:a.strStatus<b.strStatus;
}

void CConnectionsTab::OnColumn(wxListEvent& ev)
{
	m_bSortAsc=!m_bSortAsc;

	m_nSortCol = ev.m_col;

	DoSort();
	
	((CLogCtrl<CConnectionsTab>*)m_pList)->DataChanged();

}

void CConnectionsTab::OnFilterChanged()
{
	ProcessData();
}

void CConnectionsTab::DoFilter()
{
	wxString strVal;

	m_entries.erase( m_entries.begin(), m_entries.end() );

	for(size_t i=0;i<m_entriesBeforeFilter.size();i++)
	{
		bool bValid(true);

		for (size_t j=0;j<sizeof(sf)/sizeof(sf[0]);j++)
		{
			strVal = FieldToString( sf[j].iFieldID, m_entriesBeforeFilter[i], i );

			if ( !m_pSearch->Match(sf[j], strVal) )
			{
				bValid = false;
				break;
			}
		}

		if ( bValid )
		{
			m_entries.push_back( m_entriesBeforeFilter[i] );
		}
	}
}

void CConnectionsTab::DoSort()
{
	switch (m_nSortCol)
	{
		case cListColumnProto:
		{
			std::sort( m_entries.begin(), m_entries.end(), SortProto);
		}
		break;

		case cListColumnTTL:
		{
			std::sort( m_entries.begin(), m_entries.end(), SortTTL);
		}
		break;

		case cListColumnState:
		{
			std::sort( m_entries.begin(), m_entries.end(), SortState);
		}
		break;

		case cListColumnSrcCountry:
		{
			std::sort( m_entries.begin(), m_entries.end(), SortSrcCountry);
		}
		break;

		case cListColumnSrc:
		{
			std::sort( m_entries.begin(), m_entries.end(), SortSrc);
		}
		break;

		case cListColumnSrcPort:
		{
			std::sort( m_entries.begin(), m_entries.end(), SortSrcPort);
		}
		break;

		case cListColumnDstCountry:
		{
			std::sort( m_entries.begin(), m_entries.end(), SortDstCountry);
		}
		break;

		case cListColumnDst:
		{
			std::sort( m_entries.begin(), m_entries.end(), SortDst);
		}
		break;

		case cListColumnDstPort:
		{
			std::sort( m_entries.begin(), m_entries.end(), SortDstPort);
		}
		break;
	}
}

wxString CConnectionsTab::FieldToString( int column, CONNTRACENTRY& entry, int iID)
{
	wxString str;

	try
	{
		switch(column)
		{
			case cListColumnProto:
				str = entry.strProto;
			break;

			case cListColumnTTL:
				str = entry.strTTL;
			break;

			case cListColumnState:
				str = entry.strStatus;
			break;

			case cListColumnSrcCountry:
				str = entry.strSrcCountry;
			break;

			case cListColumnSrc:
				str = GetIPNAME(entry.strSrc, entry.strSrcName, iID);
			break;

			case cListColumnSrcPort:
				str = entry.strSrcPort;
			break;

			case cListColumnDstCountry:
				str = entry.strDstCountry;
			break;

			case cListColumnDst:
				str = GetIPNAME(entry.strDst, entry.strDstName, iID);
			break;

			case cListColumnDstPort:
				str = entry.strDstPort;
			break;
		}
	}
	catch (...)
	{
	}

	return str;
}

wxString CConnectionsTab::GetItemText(long item, long column, wxWindowID id)
{
	wxString str;

	try
	{
		CONNTRACENTRY& entry = m_entries[item];
		str = FieldToString( column, entry, item);
	}
	catch(...)
	{
	}

	return str;
}

void CConnectionsTab::OnResponse(const COMMANDRESPONSE& resp)
{
	if ( resp.strXML.empty() ) return;

	CMarkupSTL	doc;

	if ( doc.SetDoc( resp.strXML.c_str() ) )
	{
		if ( !doc.FindElem(CONLIST_ROOT_EL) ) return;

		m_entriesBeforeFilter.erase(m_entriesBeforeFilter.begin(), m_entriesBeforeFilter.end());

		while( doc.FindChildElem(GLOBAL_ENTRY_EL) )
		{
			CONNTRACENTRY entry;

			entry.strProto	= wxString::FromAscii( doc.GetChildAttrib(CONLIST_PROTO_ATTR).c_str() );
			entry.strSrcPort= wxString::FromAscii( doc.GetChildAttrib(CONLIST_SRC_PORT_ATTR).c_str() );
			entry.strSrc	= wxString::FromAscii( doc.GetChildAttrib(CONLIST_SRC_IP_ATTR).c_str() );
			entry.strDst	= wxString::FromAscii( doc.GetChildAttrib(CONLIST_DST_IP_ATTR).c_str() );
			entry.strDstPort= wxString::FromAscii( doc.GetChildAttrib(CONLIST_DST_PORT_ATTR).c_str() );
			entry.strTTL	= wxString::FromAscii( doc.GetChildAttrib(CONLIST_TTL_ATTR).c_str() );
			entry.strStatus = wxString::FromAscii( doc.GetChildAttrib(CONLIST_STATUS_ATTR).c_str() );
			entry.strDstCountry = GetCountry( entry.strDst );
			entry.strSrcCountry = GetCountry( entry.strSrc );

			m_entriesBeforeFilter.push_back(entry);
		}

		wxGetApp().GetMainWnd()->GetLogic().SetConntractRecordsCount( m_entriesBeforeFilter.size() );
		ProcessData();
	}
}

void CConnectionsTab::SetVal(const wxString& strIn)
{
	/*
	if ( strIn.IsEmpty() ) return;

	m_entriesBeforeFilter.erase(m_entriesBeforeFilter.begin(), m_entriesBeforeFilter.end());

	stringstream sIn( (const char*)(strIn+wxT("\n")).ToAscii() );
	{
		//CTimeDiff td("CConnectionsTab::SetVal, Parse");
		char buf[512];
		while ( !sIn.getline(buf, sizeof(buf)).eof() )
		{
			CONNTRACENTRY entry;
			if ( ParseEntry( buf, entry ) )
			{
				entry.strRaw = wxString::FromAscii(buf);
				m_entriesBeforeFilter.push_back(entry);
			}
		}
	}

	wxGetApp().GetMainWnd()->GetLogic().SetConntractRecordsCount( m_entriesBeforeFilter.size() );

	ProcessData();
	*/
}

void CConnectionsTab::Reset()
{
	time_t tEvent = time(NULL);

	m_lTotal = 0;
	m_lTCP = 0;
	m_lUDP = 0;
	m_lICMP = 0;
	m_lOther = 0;

	m_pConGraph->SetData( GRAPH_TOTAL_NAME,	0,  tEvent );
	m_pConGraph->SetData( GRAPH_TCP_NAME,	0,  tEvent );
	m_pConGraph->SetData( GRAPH_UDP_NAME,	0,  tEvent );
	m_pConGraph->SetData( GRAPH_ICMP_NAME,	0,  tEvent );
	m_pConGraph->SetData( GRAPH_OTHR_NAME,	0,  tEvent );

	m_entriesBeforeFilter.erase(m_entriesBeforeFilter.begin(), m_entriesBeforeFilter.end());
	ProcessData();
}

void CConnectionsTab::ProcessData()
{
	try
	{
		ResetSearchPos();

		{
			CTimeDiff td("CConnectionsTab::DoFilter");
			DoFilter();
		}

		{
			CTimeDiff td("CConnectionsTab::DoSort");
			DoSort();
		}

		((CLogCtrl<CConnectionsTab>*)m_pList)->DataChanged();

		ShowAnalyze();

		UpdateGraph();
	}
	catch(...)
	{
	}
}

void CConnectionsTab::UpdateGraph()
{
	//Zone
	unsigned long lConntMax = wxGetApp().GetMainWnd()->GetLogic().GetConntrackMAX();

	if ( lConntMax )
	{
		ZONERANGE zone;
		ZONELIST list;

		//Bad
		zone.color = BAD_COLOR;
		zone.nFrom = 0.9*lConntMax;
		zone.nTo   = 100*lConntMax;
		list.push_back(zone);

		//Fair
		zone.color = FAIR_COLOR;
		zone.nFrom = 0.8*lConntMax;
		zone.nTo   = 0.9*lConntMax;
		list.push_back(zone);

		//Good
		zone.color = GOOD_COLOR;
		zone.nFrom = 0.5*lConntMax;
		zone.nTo   = 0.8*lConntMax;
		list.push_back(zone);

		//Excelent
		zone.color = EXCELENT_COLOR;
		zone.nFrom = 0;
		zone.nTo   = 0.5*lConntMax;
		list.push_back(zone);
		m_pConGraph->SetZones(list);
	}

	time_t tEvent = time(NULL);
	size_t tTCP(0), tUDP(0), tICMP(0), tOther(0);

	wxString str;

	for(size_t i=0;i<m_entriesBeforeFilter.size();++i)
	{
		const CONNTRACENTRY& cv = m_entriesBeforeFilter[i];
		str = cv.strProto.Lower();
		
		if ( wxNOT_FOUND!=str.find(wxT("tcp")) )
		{
			++tTCP;
		}
		else
		if ( wxNOT_FOUND!=str.find(wxT("udp")) )
		{
			++tUDP;
		}
		else
		if ( wxNOT_FOUND!=str.find(wxT("icmp")) )
		{
			++tICMP;
		}
		else
		{
			++tOther;
		}
	}

	m_lTotal = tTCP+tUDP+tICMP+tOther;
	m_lTCP = tTCP;
	m_lUDP = tUDP;
	m_lICMP = tICMP;
	m_lOther = tOther;

	m_pConGraph->SetData( GRAPH_TOTAL_NAME,	m_lTotal,  tEvent );
	m_pConGraph->SetData( GRAPH_TCP_NAME,	m_lTCP,	tEvent );
	m_pConGraph->SetData( GRAPH_UDP_NAME,	m_lUDP,	tEvent );
	m_pConGraph->SetData( GRAPH_ICMP_NAME,	m_lICMP,  tEvent );
	m_pConGraph->SetData( GRAPH_OTHR_NAME,	m_lOther, tEvent );


}

//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//Search
void CConnectionsTab::GetSearchFields(SEARCHFIELD*& sfRes, size_t& sz)
{
	sfRes = sf;
	sz = sizeof(sf)/sizeof(sf[0]);
}

//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//IGUISupport
wxString CConnectionsTab::Save()
{
	wxString str;

	for( size_t i=0;i<m_entries.size();i++ )
	{
		CONNTRACENTRY& entry = m_entries[i];

		if ( entry.strRaw.empty() )
		{
			wxString strTemp;

			strTemp = wxString::Format( wxT("%s: %s\t%s: %s\t%s: %s\t%s: %s\t%s: %s\t%s: %s\t%s: %s\t%s: %s"),
				_("Protocol"), entry.strProto.c_str(),
				_("TTL"), entry.strTTL.c_str(),
				_("Src"), GetIPNAME(entry.strSrc, entry.strSrcName).c_str(),
				_("Src Port"), entry.strSrcPort.c_str(),
				_("Src Country"), entry.strSrcCountry.c_str(),
				_("Dst"), GetIPNAME(entry.strDst, entry.strDstName).c_str(),
				_("Dst Port"), entry.strDstPort.c_str(),
				_("Dst Country"), entry.strDstCountry .c_str() );

			str+=strTemp;
		}
		else
		{
			str+=entry.strRaw;
		}

		#ifdef LINUX
			str+=wxT("\n");
		#else
			str+=wxT("\r\n");
		#endif
	}

	return str;
}

//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//Analyze
void CConnectionsTab::ShowAnalyze()
{
	FillTimeOutTable();

	if ( m_pAnylizeWindow )
	{
		wxString strTemplate(wxT("<html><body>%s</body></html>"));
		wxString str;

		str+=wxT("<center><b><font size='4'>");
		str+=_("Performance and Optimization Tips");
		str+=wxT("</font></b></center>");

		bool bProblemFound(false);

		str+=wxT("<table>");

		str+=wxT("<table border='0' style='border-collapse: collapse' cellspacing='0' cellpadding='2' width='100%'>");
		str+=wxT("<tr bgcolor='#84CAF9'><th align='left'><b>");
		str+=_("Problem");
		str+=wxT("</b></th><th></th><th align='left'><b>");
		str+=_("How to fix it");
		str+=wxT("</b></th></tr>");

		int nOrder = 0;

		time_t tNow = time(NULL);

		if ( !m_entriesBeforeFilter.empty() )
		{
			size_t stAll = m_entriesBeforeFilter.size();
			size_t stUDPTTL;
			size_t stUDP = GetProtoCountAndTTL(wxT("udp"), wxT(""), stUDPTTL);

			unsigned long lConntMax = wxGetApp().GetMainWnd()->GetLogic().GetConntrackMAX();

			if ( lConntMax>1 )
			{
				double bPerc = 100.0*double(stUDP)/double(lConntMax?lConntMax:1);

				//UDP connections total more than 50%
				if ( lConntMax && bPerc>=50.0 )
				{
					wxString strTmp, strLabel = _("Too many UDP connections");
					strLabel+=wxT("[");
					strLabel+=strTmp.Format(wxT("%d of %d, %2.0f%%"), stUDP, lConntMax, bPerc);
					strLabel+=wxT("]");

					str+=BuildTableLine(strLabel,
						_("uTorrent users have to disable uTP protocol, go to Advanced Preferences and set bt.transp_disposition to 5.\nIf you use BitTorrent's DHT, try to disable it also."), nOrder);

					bProblemFound = true;
				}
			
				NATTIMEOUTS natDef;
				NATTIMEOUTS natTimeoutsRouter = wxGetApp().GetMainWnd()->GetLogic().GetActualTCPTimeOuts();

				if ( natTimeoutsRouter.stUDP>natDef.stUDP )
				{
					wxString strFix(wxT("<a href='fix_nat_udp_timeout'>"));
					strFix+=_("Click to Fix it!");
					strFix+=wxT("</a>");

					wxString strTmp, strLabel = _("NAT UDP timeout is too high");
					strLabel+=wxT("[");
					strLabel+=strTmp.Format(wxT("%d "), stUDPTTL);
					strLabel+=_("sec");
					strLabel+=wxT("]");

					str+=BuildTableLine(strLabel,
						strFix, nOrder);

					bProblemFound = true;
				}
			}

			/////////////////////////////////////////////////////////////
			//TCP time outs checker
			//size_t stTmp;
			if ( !wxGetApp().GetMainWnd()->GetPropStorage().GetEnableWWWOpt() )
			{
				for( size_t i=0;i<sizeof(tcpCheck)/sizeof( tcpCheck[0] );i++ )
				{
					const TCPCHECK& entry = tcpCheck[i];
					//GetProtoCountAndTTL(wxT("tcp"), entry.strSTATE, stTmp );

					if ( entry.stTimeOutRouter>entry.stTimeOutNormal )
					{
						wxString strFix(wxT("<a href='"));
						strFix+=entry.strAction;
						strFix+=wxT("'>");
						strFix+=_("Click to Fix it!");
						strFix+=wxT("</a>");

						wxString strTmp, strLabel;
						strLabel = wxString::Format(_("NAT TCP %s timeout is too high[%d instead of %d]"),
							entry.strSTATE,
							entry.stTimeOutRouter,
							entry.stTimeOutNormal );
					
						str+=BuildTableLine(strLabel,
							strFix, nOrder);

						bProblemFound = true;
					}
				}
			}
		}

		str+=wxT("</table>");

		m_pAnylizeWindow->SetPage( wxString::Format(strTemplate,str.c_str()) );

		m_pAnylizeWindow->Show( bProblemFound );

		GetSizer()->FitInside(this);

		GetSizer()->Layout();
	}
}

size_t CConnectionsTab::GetProtoCountAndTTL(const wxString& strProto,
											const wxString& strState,
											size_t& stTTL)
{
	size_t stRet(0);

	stTTL = 0;

	for (size_t i=0;i<m_entriesBeforeFilter.size();i++)
	{
		const CONNTRACENTRY& entry = m_entriesBeforeFilter[i];

		if ( entry.strProto == strProto && 
			(strState.empty() || strState==entry.strStatus ) )
		{
			++stRet;

			size_t ttl = (size_t)atol( entry.strTTL.ToAscii() );

			if ( ttl>stTTL )
			{
				stTTL = ttl;
			}
		}
	}

	return stRet;
}

wxString CConnectionsTab::BuildTableLine(const wxString& strProblem,
										 const wxString& strSolution, int& nOrder)
{
	wxString str;

	if ( (nOrder%2)!=0)
	{
		str+=wxT("<tr bgcolor='#F0F0F0'>");
	}
	else
	{
		str+=wxT("<tr>");
	}

	str+=wxT("<td><img src='");
	str+=wxGetApp().GetImageDir()+wxT("perfomance_error.png");
	str+=wxT("'>&nbsp;<b>");
	
	str+=strProblem;

	str+=wxT("</b></td><td></td>");
	str+=wxT("<td>");

	str+=strSolution;

	str+=wxT("</td>");
	str+=wxT("</tr>");

	++nOrder;

	return str;
}

void CConnectionsTab::FillTimeOutTable()
{
	NATTIMEOUTS tcpTimeoutsRouter = wxGetApp().GetMainWnd()->GetLogic().GetActualTCPTimeOuts();

	for( size_t i=0;i<sizeof(tcpCheck)/sizeof( tcpCheck[0] );i++ )
	{
		TCPCHECK& entry = tcpCheck[i];

		if ( wxString(entry.strAction)==wxT("ip_conntrack_generic_timeout") )
		{
			entry.stTimeOutRouter = tcpTimeoutsRouter.stGeneric;
		}
		else
		if ( wxString(entry.strAction)==wxT("ip_conntrack_tcp_timeout_established") )
		{
			entry.stTimeOutRouter = tcpTimeoutsRouter.stESTABLISHED;
		}
		else
		if ( wxString(entry.strAction)==wxT("ip_conntrack_tcp_timeout_syn_sent") )
		{
			entry.stTimeOutRouter = tcpTimeoutsRouter.stSYN_SENT;
		}
		else
		if ( wxString(entry.strAction)==wxT("ip_conntrack_tcp_timeout_syn_recv") )
		{
			entry.stTimeOutRouter = tcpTimeoutsRouter.stSYN_RECV;
		}
		else
		if ( wxString(entry.strAction)==wxT("ip_conntrack_tcp_timeout_fin_wait") )
		{
			entry.stTimeOutRouter = tcpTimeoutsRouter.stFIN_WAIT;
		}
		else
		if ( wxString(entry.strAction)==wxT("ip_conntrack_tcp_timeout_time_wait") )
		{
			entry.stTimeOutRouter = tcpTimeoutsRouter.stTIME_WAIT;
		}
		else
		if ( wxString(entry.strAction)==wxT("ip_conntrack_tcp_timeout_close") )
		{
			entry.stTimeOutRouter = tcpTimeoutsRouter.stCLOSE;
		}
		else
		if ( wxString(entry.strAction)==wxT("ip_conntrack_tcp_timeout_close_wait") )
		{
			entry.stTimeOutRouter = tcpTimeoutsRouter.stCLOSE_WAIT;
		}
		else
		if ( wxString(entry.strAction)==wxT("ip_conntrack_tcp_timeout_last_ack") )
		{
			entry.stTimeOutRouter = tcpTimeoutsRouter.stLAST_ACK;
		}
		else
		{
			assert(0);
		}
	}
}


//m_wMain.GetPropStorage().GetEnableAdvancedOpt()
void CConnectionsTab::OnCLick(wxHtmlLinkEvent& event)
{
	wxString strURL =  event.GetLinkInfo().GetHref();

	if ( strURL==wxT("fix_nat_udp_timeout") )
	{
		long lVal = wxGetNumberFromUser(
						_("Input NAT UDP timeout value in seconds, recommended value 60.\nYou can change it late on using Tools\\Otions\\General\\Advanced button."),
						_("UDT Timeout[ip_conntrack_udp_timeout]:"), _("Input NAT UDP timeout value"),
						60, 1, 60*60*24, this);

		if ( -1!=lVal )
		{
			wxString strVal = wxString::Format(wxT("%d"), lVal);
			SaveAndExecuteNewTimeOuts(wxT("ip_conntrack_udp_timeout"), strVal );
		}
	}
	else
	{
		for( size_t i=0;i<sizeof(tcpCheck)/sizeof( tcpCheck[0] );i++ )
		{
			const TCPCHECK& entry = tcpCheck[i];

			if ( entry.strAction == strURL )
			{
				wxString strTitle;
				
				strTitle = wxString::Format(_("Input NAT %s TCP timeout value in seconds.\nYou can change it late on using Tools\\Otions\\General\\Advanced button."),
					entry.strSTATE );

				long lVal = wxGetNumberFromUser(strTitle,
						_("Timeout:"), _("Input timeout value"),
						entry.stTimeOutNormal, 1, 60*60*24, this);

				if ( -1!=lVal )
				{
					wxString strVal = wxString::Format(wxT("%d"), lVal);
					SaveAndExecuteNewTimeOuts(entry.strAction, strVal);
				}
				break;
			}
		}
	}
}

void CConnectionsTab::SaveAndExecuteNewTimeOuts(const wxString& strField,
												const wxString& strVal)
{
	wxGetApp().GetMainWnd()->GetPropStorage().SetEnableNATOpt( false );
	wxGetApp().GetMainWnd()->GetPropStorage().SetEnableTCPOpt( false );
	wxGetApp().GetMainWnd()->GetPropStorage().SetEnableP2PNetworkOpt( false );
	wxGetApp().GetMainWnd()->GetPropStorage().SetEnableICMPOpt( false );

	//Advanced options was not enabled before, keep original router's timeouts
	if ( !wxGetApp().GetMainWnd()->GetPropStorage().GetEnableAdvancedOpt() )
	{
		for( size_t i=0;i<sizeof(tcpCheck)/sizeof( tcpCheck[0] );i++ )
		{
			const TCPCHECK& entry = tcpCheck[i];

			if ( entry.strAction != strField &&
				 entry.stTimeOutRouter>0 &&
				 entry.stTimeOutRouter<=entry.stTimeOutNormal )
			{
				wxString strTimeOut = wxString::Format(wxT("%d"), entry.stTimeOutRouter);
				wxGetApp().GetMainWnd()->GetPropStorage().SetAdvancedOptListProperty(entry.strAction, strTimeOut);
			}
		}
	}

	wxGetApp().GetMainWnd()->GetPropStorage().SetAdvancedOptListProperty(strField, strVal);


	wxGetApp().GetMainWnd()->GetPropStorage().SetEnableAdvancedOpt( true );

	wxGetApp().GetMainWnd()->GetPropStorage().Save();

	wxGetApp().GetMainWnd()->GetLogic().DoStartUpAction();
	wxGetApp().GetMainWnd()->GetLogic().Refresh();
}


void CConnectionsTab::OnInfoUI(wxUpdateUIEvent& event)
{
	event.Enable(1==m_pList->GetSelectedItemCount());
}

void CConnectionsTab::OnInfo(wxCommandEvent& event)
{
	long item = -1;
	item = m_pList->GetNextItem(item, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);

	if ( item != -1 )
	{
		wxString strIP = GetItemText(item, cListColumnSrc);

		if ( 0==strIP.Find(wxT("192.168.")) ||
			 0==strIP.Find(wxT("172.16.")) ||
			 0==strIP.Find(wxT("10.")) )
		{
			strIP = GetItemText(item, cListColumnDst);
		}


		wxString strURL = wxT("http://www.routercommander.com/index.php?option=com_jumi&fileid=3&src=");
		strURL+=strIP;

		wxLaunchDefaultBrowser(strURL);
	}
}

int CConnectionsTab::GetItemColumnImage(long item, long column, wxWindowID id/*=wxID_ANY*/)
{
	int nRes=-1;

	if ( cListColumnSrcCountry == column || cListColumnDstCountry == column)
	{
		try
		{
			CONNTRACENTRY& entry = m_entries[item];
			wxString strIP = cListColumnSrcCountry==column?entry.strSrc:entry.strDst;

			if ( wxNOT_FOUND==strIP.Find(wxT("192.168.")) &&
				 wxNOT_FOUND==strIP.Find(wxT("172.16.")) &&
				 wxNOT_FOUND==strIP.Find(wxT("10.")) )
			{
				unsigned long lAddr =  htonl( inet_addr( strIP.ToAscii() ) );
				nRes = m_ip2Country.GetFlag( lAddr );
			}
		}
		catch(...)
		{
		}
	}

	return nRes;
}

wxString CConnectionsTab::GetCountry( const wxString& strIP )
{
	wxString strCountry;

	if ( wxNOT_FOUND==strIP.Find(wxT("192.168.")) &&
		 wxNOT_FOUND==strIP.Find(wxT("172.16.")) &&
		 wxNOT_FOUND==strIP.Find(wxT("10.")) )
	{
		unsigned long lAddr =  htonl( inet_addr( strIP.ToAscii() ) );
		strCountry = m_ip2Country.GetCountry( lAddr );
	}

	return strCountry;
}

wxString CConnectionsTab::GetIPNAME(const wxString& strIP, wxString& strRes, int iID)
{
	if ( !m_bResolve ) return strIP;

	if ( !strRes.empty() )
	{
		return strRes;
	}

	unsigned long lAddr =  inet_addr( strIP.ToAscii() );

	wxString strResolved;
	if ( m_resolver.Resolve(lAddr, (void*)iID, strResolved) )
	{
		strRes = strResolved;
		return strRes;
	}

	return strIP;
}

wxString CConnectionsTab::GetLog()
{
	wxString strStatus;

	if ( wxGetApp().GetMainWnd()->GetPropStorage().GetEnableConnectionsLog() )
	{
		strStatus = wxString::Format( wxT("%s %u, %s %u, %s %u, %s %u, %s %u"),
			_("Connections Total:"), m_lTotal,
			_("TCP/IP:"), m_lTCP,
			_("UDP:"), m_lUDP,
			_("ICMP:"), m_lICMP,
			_("Other:"), m_lOther );
	}

	return strStatus;
}

//////////////////////////////////////////////////////////////////////////
void CConnectionsTab::OnResolveCheckBox(wxCommandEvent& event)
{
	m_bResolve = m_pResolveNames->GetValue();
	wxGetApp().GetMainWnd()->GetConfig().SetIPResolve(m_bResolve);
	((CLogCtrl<CConnectionsTab>*)m_pList)->DataChanged();

	if (!m_bResolve)
	{
		m_resolver.CleanJobList();
	}
}

void CConnectionsTab::OnResolve(unsigned long lIP, const wxString& strDNSName, void* pCookie)
{
	if ( m_pList && m_bResolve )
	{
		int item = (int)pCookie;
		m_pList->RefreshItem( item );
	}
}